package com.sparrow.common.cron;

import com.sparrow.common.cron.annotation.Cron;
import com.sparrow.common.cron.domain.CronTask;
import com.sparrow.common.schedule.FixTimeScheduledThreadPoolExecutor;
import com.sparrow.common.thread.factory.SimpleThreadFactory;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * Cron Task Manager
 */
@Component
public class CronManager implements BeanPostProcessor, DisposableBean, ApplicationListener<ContextRefreshedEvent> {

    private static Logger logger = LoggerFactory.getLogger(CronManager.class);

    private List<CronTask> taskList = new ArrayList<>();

    private ScheduledExecutorService executor = new FixTimeScheduledThreadPoolExecutor(1, new SimpleThreadFactory("Cron"));

    private volatile boolean started = false;

    private ConcurrentHashMap<CronTask, ScheduledFuture<?>> futures = new ConcurrentHashMap<>();
    private volatile boolean shuttingDown;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        ReflectionUtils.doWithMethods(bean.getClass(), method -> {
            Cron cron = method.getAnnotation(Cron.class);
            CronTask task = new CronTask(bean, method);
            try {
                task.setCronExpression(new CronExpression(cron.value()));
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
            task.initTriggerTime();
            taskList.add(task);
        }, method -> method.isAnnotationPresent(Cron.class));
        return bean;
    }

    public void start() {
        for (CronTask cronTask : taskList) {
            scheduleTask(cronTask);
        }
    }

    private void scheduleTask(CronTask cronTask) {
        if (shuttingDown) return;

        ScheduledFuture<?> future = executor.schedule(() -> {
            try {
                cronTask.run();
            } catch (Exception e) {
                logger.error("CronTask执行异常", e);
            }
            cronTask.initTriggerTime();
            if (cronTask.getNextTime() >= System.currentTimeMillis()) {
                scheduleTask(cronTask);
            }
        }, cronTask.getDelay(), TimeUnit.MILLISECONDS);

        futures.put(cronTask, future);
    }

    @Override
    public void destroy() throws Exception {
        shuttingDown = true;
        for (ScheduledFuture<?> future : futures.values()) {
            future.cancel(true);
        }
        this.executor.shutdown();
        this.executor.awaitTermination(30, TimeUnit.MINUTES);
        logger.info("CronManager 关闭");
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 避免多次触发，因为ContextRefreshedEvent可能多次发布
        if (!started) {
            this.start();
            started = true;
        }
    }
}
